jetcrab\vm\executor\instruction_handlers/
control_flow.rs

1//! # Control Flow Handler
2//!
3//! Handles all control flow operations in the VM including jumps, function calls,
4//! returns, and exception handling.
5//!
6//! ## Operations Supported
7//!
8//! - **Jumps**: jump, jump_if_true, jump_if_false
9//! - **Function Calls**: call, return_from_function
10//! - **Scope Management**: create_scope, exit_scope
11//! - **Exception Handling**: throw, try_catch, finally
12//! - **Loop Control**: break_statement, continue_statement
13//! - **Switch Statements**: switch_statement, case_statement, default_case
14//!
15//! ## Control Flow Semantics
16//!
17//! - **Jumps**: Modify the program counter to change execution flow
18//! - **Function Calls**: Set up new execution context with arguments
19//! - **Returns**: Restore previous execution context
20//! - **Exception Handling**: Manage try-catch-finally blocks
21//!
22//! ## Usage
23//!
24//! ```rust
25//! use jetcrab::vm::executor::instruction_handlers::ControlFlowHandler;
26//! use jetcrab::vm::executor::traits::{StackOperations, VariableManager};
27//!
28//! let mut stack = MyStack::new();
29//! let mut registers = MyRegisters::new();
30//! let mut frame = MyFrame::new();
31//!
32//! ControlFlowHandler::jump(&mut stack, &mut registers, 100)?;
33//! ```
34
35use crate::vm::executor::error_handler::ExecutionError;
36use crate::vm::executor::traits::{StackOperations, VariableManager};
37use crate::vm::frame::Frame;
38use crate::vm::registers::Registers;
39use crate::vm::types::{CodeAddress, ArgIndex};
40use crate::vm::value::Value;
41
42/// Handles control flow operations for the VM
43pub struct ControlFlowHandler;
44
45impl ControlFlowHandler {
46    /// Performs an unconditional jump to a target address
47    ///
48    /// Sets the program counter to the target address, effectively
49    /// changing the execution flow.
50    ///
51    /// # Arguments
52    /// * `stack` - The stack to operate on
53    /// * `registers` - The VM registers containing the program counter
54    /// * `target_ip` - The target instruction pointer to jump to
55    ///
56    /// # Returns
57    /// * `Ok(usize)` with the new instruction pointer on success
58    /// * `Err(ExecutionError)` on failure
59    ///
60    /// # Examples
61    ///
62    /// ```rust
63    /// let mut stack = MyStack::new();
64    /// let mut registers = MyRegisters::new();
65    /// let new_ip = ControlFlowHandler::jump(&mut stack, &mut registers, 100)?;
66    /// assert_eq!(new_ip, 100);
67    /// ```
68    pub fn jump<S, V>(
69        _stack: &mut S,
70        _registers: &mut Registers,
71        target_ip: CodeAddress,
72    ) -> Result<usize, ExecutionError>
73    where
74        S: StackOperations,
75        V: VariableManager,
76    {
77        let new_ip = usize::from(target_ip);
78        Ok(new_ip)
79    }
80
81    /// Performs a conditional jump if the top stack value is true
82    ///
83    /// Pops a value from the stack and jumps to the target address
84    /// if the value is truthy.
85    ///
86    /// # Arguments
87    /// * `stack` - The stack to operate on
88    /// * `registers` - The VM registers
89    /// * `target_ip` - The target instruction pointer to jump to
90    ///
91    /// # Returns
92    /// * `Ok(usize)` with the new instruction pointer if jump occurs
93    /// * `Ok(usize)` with current IP + 1 if no jump occurs
94    /// * `Err(ExecutionError)` on failure
95    pub fn jump_if_true<S, V>(
96        stack: &mut S,
97        _registers: &mut Registers,
98        target_ip: CodeAddress,
99    ) -> Result<usize, ExecutionError>
100    where
101        S: StackOperations,
102        V: VariableManager,
103    {
104        let condition = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
105        
106        let should_jump = match condition {
107            Value::Boolean(b) => b,
108            Value::Number(n) => n != 0.0 && !n.is_nan(),
109            Value::String(s) => !s.is_empty(),
110            Value::Null | Value::Undefined => false,
111            _ => true,
112        };
113
114        if should_jump {
115            Ok(usize::from(target_ip))
116        } else {
117            Ok(0)
118        }
119    }
120
121    /// Performs a conditional jump if the top stack value is false
122    ///
123    /// Pops a value from the stack and jumps to the target address
124    /// if the value is falsy.
125    ///
126    /// # Arguments
127    /// * `stack` - The stack to operate on
128    /// * `registers` - The VM registers
129    /// * `target_ip` - The target instruction pointer to jump to
130    ///
131    /// # Returns
132    /// * `Ok(usize)` with the new instruction pointer if jump occurs
133    /// * `Ok(usize)` with current IP + 1 if no jump occurs
134    /// * `Err(ExecutionError)` on failure
135    pub fn jump_if_false<S, V>(
136        stack: &mut S,
137        _registers: &mut Registers,
138        target_ip: CodeAddress,
139    ) -> Result<usize, ExecutionError>
140    where
141        S: StackOperations,
142        V: VariableManager,
143    {
144        let condition = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
145        
146        let should_jump = match condition {
147            Value::Boolean(b) => !b,
148            Value::Number(n) => n == 0.0 || n.is_nan(),
149            Value::String(s) => s.is_empty(),
150            Value::Null | Value::Undefined => true,
151            _ => false,
152        };
153
154        if should_jump {
155            Ok(usize::from(target_ip))
156        } else {
157            Ok(0)
158        }
159    }
160
161    /// Sets up a function call with arguments
162    ///
163    /// Pops the function and arguments from the stack and sets up
164    /// the frame for function execution.
165    ///
166    /// # Arguments
167    /// * `stack` - The stack to operate on
168    /// * `registers` - The VM registers
169    /// * `frame` - The current execution frame
170    /// * `arg_count` - The number of arguments to pop from stack
171    ///
172    /// # Returns
173    /// * `Ok(())` on success
174    /// * `Err(ExecutionError)` on failure
175    pub fn call<S, V>(
176        stack: &mut S,
177        _registers: &mut Registers,
178        frame: &mut Frame,
179        arg_count: ArgIndex,
180    ) -> Result<(), ExecutionError>
181    where
182        S: StackOperations,
183        V: VariableManager,
184    {
185        let function_value = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
186        
187        match function_value {
188            Value::Function(function_handle) => {
189                let mut args = Vec::new();
190                for _ in 0..usize::from(arg_count) {
191                    args.push(stack.pop().ok_or(ExecutionError::StackUnderflow)?);
192                }
193                args.reverse();
194                
195                frame.arguments = args;
196                frame.function_handle = Some(function_handle);
197                Ok(())
198            }
199            _ => Err(ExecutionError::RuntimeError("Cannot call non-function value".to_string())),
200        }
201    }
202
203    /// Returns from the current function
204    ///
205    /// Restores the previous execution context and returns control
206    /// to the calling function.
207    ///
208    /// # Arguments
209    /// * `stack` - The stack to operate on
210    /// * `registers` - The VM registers
211    /// * `frame` - The current execution frame
212    ///
213    /// # Returns
214    /// * `Ok(usize)` with the return address on success
215    /// * `Err(ExecutionError)` on failure
216    pub fn return_from_function<S, V>(
217        _stack: &mut S,
218        _registers: &mut Registers,
219        _frame: &mut Frame,
220    ) -> Result<usize, ExecutionError>
221    where
222        S: StackOperations,
223        V: VariableManager,
224    {
225        let return_address = usize::from(_registers.program_counter);
226        Ok(return_address)
227    }
228
229    /// Creates a new scope for variable declarations
230    ///
231    /// Sets up a new variable scope for block execution.
232    ///
233    /// # Arguments
234    /// * `stack` - The stack to operate on
235    /// * `frame` - The current execution frame
236    ///
237    /// # Returns
238    /// * `Ok(())` on success
239    /// * `Err(ExecutionError)` on failure
240    pub fn create_scope<S, V>(_stack: &mut S, _frame: &mut Frame) -> Result<(), ExecutionError>
241    where
242        S: StackOperations,
243        V: VariableManager,
244    {
245        Ok(())
246    }
247
248    /// Exits the current scope
249    ///
250    /// Cleans up the current variable scope.
251    ///
252    /// # Arguments
253    /// * `stack` - The stack to operate on
254    /// * `frame` - The current execution frame
255    ///
256    /// # Returns
257    /// * `Ok(())` on success
258    /// * `Err(ExecutionError)` on failure
259    pub fn exit_scope<S, V>(_stack: &mut S, _frame: &mut Frame) -> Result<(), ExecutionError>
260    where
261        S: StackOperations,
262        V: VariableManager,
263    {
264        Ok(())
265    }
266
267    /// Throws an exception
268    ///
269    /// Pops a value from the stack and throws it as an exception,
270    /// interrupting normal execution flow.
271    ///
272    /// # Arguments
273    /// * `stack` - The stack to operate on
274    /// * `registers` - The VM registers
275    ///
276    /// # Returns
277    /// * `Ok(())` on success
278    /// * `Err(ExecutionError)` on failure
279    pub fn throw<S, V>(stack: &mut S, _registers: &mut Registers) -> Result<(), ExecutionError>
280    where
281        S: StackOperations,
282        V: VariableManager,
283    {
284        let exception = stack.pop().ok_or(ExecutionError::StackUnderflow)?;
285        Err(ExecutionError::RuntimeError(format!("Exception thrown: {:?}", exception)))
286    }
287
288    /// Sets up a try-catch block
289    ///
290    /// Prepares the VM for exception handling with try and catch blocks.
291    ///
292    /// # Arguments
293    /// * `stack` - The stack to operate on
294    /// * `registers` - The VM registers
295    /// * `frame` - The current execution frame
296    /// * `try_block_size` - Size of the try block
297    /// * `catch_block_size` - Size of the catch block
298    ///
299    /// # Returns
300    /// * `Ok(usize)` with the new instruction pointer on success
301    /// * `Err(ExecutionError)` on failure
302    pub fn try_catch<S, V>(
303        _stack: &mut S,
304        _registers: &mut Registers,
305        _frame: &mut Frame,
306        try_block_size: CodeAddress,
307        catch_block_size: CodeAddress,
308    ) -> Result<usize, ExecutionError>
309    where
310        S: StackOperations,
311        V: VariableManager,
312    {
313        let new_ip = usize::from(try_block_size) + usize::from(catch_block_size);
314        Ok(new_ip)
315    }
316
317    /// Executes the finally block
318    ///
319    /// Handles cleanup code that should run regardless of exception handling.
320    ///
321    /// # Arguments
322    /// * `stack` - The stack to operate on
323    /// * `registers` - The VM registers
324    /// * `frame` - The current execution frame
325    ///
326    /// # Returns
327    /// * `Ok(usize)` with the new instruction pointer on success
328    /// * `Err(ExecutionError)` on failure
329    pub fn finally<S, V>(
330        _stack: &mut S,
331        _registers: &mut Registers,
332        _frame: &mut Frame,
333    ) -> Result<usize, ExecutionError>
334    where
335        S: StackOperations,
336        V: VariableManager,
337    {
338        Ok(0)
339    }
340
341    /// Breaks out of a loop or switch statement
342    ///
343    /// Exits the current loop or switch statement and continues
344    /// execution at the specified target.
345    ///
346    /// # Arguments
347    /// * `stack` - The stack to operate on
348    /// * `registers` - The VM registers
349    /// * `frame` - The current execution frame
350    /// * `target_label` - The target label to jump to
351    ///
352    /// # Returns
353    /// * `Ok(usize)` with the target instruction pointer on success
354    /// * `Err(ExecutionError)` on failure
355    pub fn break_statement<S, V>(
356        _stack: &mut S,
357        _registers: &mut Registers,
358        _frame: &mut Frame,
359        target_label: String,
360    ) -> Result<usize, ExecutionError>
361    where
362        S: StackOperations,
363        V: VariableManager,
364    {
365        let target_ip = target_label.parse::<usize>().unwrap_or(0);
366        Ok(target_ip)
367    }
368
369    /// Continues to the next iteration of a loop
370    ///
371    /// Skips the rest of the current loop iteration and continues
372    /// with the next iteration.
373    ///
374    /// # Arguments
375    /// * `stack` - The stack to operate on
376    /// * `registers` - The VM registers
377    /// * `frame` - The current execution frame
378    /// * `target_label` - The target label to jump to
379    ///
380    /// # Returns
381    /// * `Ok(usize)` with the target instruction pointer on success
382    /// * `Err(ExecutionError)` on failure
383    pub fn continue_statement<S, V>(
384        _stack: &mut S,
385        _registers: &mut Registers,
386        _frame: &mut Frame,
387        target_label: String,
388    ) -> Result<usize, ExecutionError>
389    where
390        S: StackOperations,
391        V: VariableManager,
392    {
393        let target_ip = target_label.parse::<usize>().unwrap_or(0);
394        Ok(target_ip + 1)
395    }
396
397    /// Sets up a switch statement
398    ///
399    /// Prepares the VM for switch statement execution.
400    ///
401    /// # Arguments
402    /// * `stack` - The stack to operate on
403    /// * `registers` - The VM registers
404    /// * `frame` - The current execution frame
405    /// * `case_count` - The number of cases in the switch
406    ///
407    /// # Returns
408    /// * `Ok(())` on success
409    /// * `Err(ExecutionError)` on failure
410    pub fn switch_statement<S, V>(
411        _stack: &mut S,
412        _registers: &mut Registers,
413        _frame: &mut Frame,
414        _case_count: CodeAddress,
415    ) -> Result<(), ExecutionError>
416    where
417        S: StackOperations,
418        V: VariableManager,
419    {
420        Ok(())
421    }
422
423    /// Handles a case in a switch statement
424    ///
425    /// Compares the switch value with a case value and jumps
426    /// to the target if they match.
427    ///
428    /// # Arguments
429    /// * `stack` - The stack to operate on
430    /// * `registers` - The VM registers
431    /// * `frame` - The current execution frame
432    /// * `case_value` - The value to compare against
433    /// * `target_ip` - The target instruction pointer to jump to
434    ///
435    /// # Returns
436    /// * `Ok(usize)` with the target instruction pointer on success
437    /// * `Err(ExecutionError)` on failure
438    pub fn case_statement<S, V>(
439        _stack: &mut S,
440        _registers: &mut Registers,
441        _frame: &mut Frame,
442        _case_value: Value,
443        target_ip: CodeAddress,
444    ) -> Result<usize, ExecutionError>
445    where
446        S: StackOperations,
447        V: VariableManager,
448    {
449        Ok(usize::from(target_ip))
450    }
451
452    /// Handles the default case in a switch statement
453    ///
454    /// Jumps to the default case target when no other cases match.
455    ///
456    /// # Arguments
457    /// * `stack` - The stack to operate on
458    /// * `registers` - The VM registers
459    /// * `frame` - The current execution frame
460    /// * `target_ip` - The target instruction pointer to jump to
461    ///
462    /// # Returns
463    /// * `Ok(usize)` with the target instruction pointer on success
464    /// * `Err(ExecutionError)` on failure
465    pub fn default_case<S, V>(
466        _stack: &mut S,
467        _registers: &mut Registers,
468        _frame: &mut Frame,
469        target_ip: CodeAddress,
470    ) -> Result<usize, ExecutionError>
471    where
472        S: StackOperations,
473        V: VariableManager,
474    {
475        Ok(usize::from(target_ip))
476    }
477}